<!---
Copyright: (c) 2006 The MachBlog Authors
Authors: Matt Woodward (mpwoodward@gmail.com) & Peter J. Farrell (pjf@maestropublishing.com)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at 

http://www.apache.org/licenses/LICENSE-2.0 

Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
See the License for the specific language governing permissions and 
limitations under the License.

$Id: udfs.cfc 1197 2007-07-19 19:00:25Z pfarrell $

Notes:
--->
<cfcomponent
	displayname="UDFs"
	output="false"
	hint="Wrapper for udfs.">
	
	<!---
	PROPERTIES
	--->
	<cfset variables.instance = StructNew() />

	<!---
	INITIALIZATION / CONFIGURATION
	--->
	<cffunction name="init" access="public" returntype="udfs" output="false"
		hint="Initializes the udfs wrapper.">
		<cfargument name="eventParameter" type="string" required="true" />
		<cfargument name="blogUrl" type="string" required="true" />
		<cfargument name="defaultUrlBase" type="string" required="true" />
		<cfargument name="urlDelimiters" type="string" required="true" />
		<cfargument name="moduleDelimiter" type="string" required="true" />
		
		<!--- Run setters --->
		<cfset setEventParameter(arguments.eventParameter) />
		<cfset setBlogUrl(arguments.blogUrl) />
		<cfif Len(arguments.defaultUrlBase)>
			<cfset setDefaultUrlBase(arguments.defaultUrlBase) />
		<cfelse>
			<cfset setDefaultUrlBase("index.cfm") />
		</cfif>
		<cfif ListLen(arguments.urlDelimiters, "|") EQ 3>
			<cfset setQueryStringDelimiter(ListGetAt(arguments.urlDelimiters, 1, "|")) />
			<cfset setSeriesDelimiter(ListGetAt(arguments.urlDelimiters, 2, "|")) />
			<cfset setPairDelimiter(ListGetAt(arguments.urlDelimiters, 3, "|")) />
		<cfelse>
			<cfset setQueryStringDelimiter("?") />
			<cfset setSeriesDelimiter("=") />
			<cfset setPairDelimiter("&") />		
		</cfif>
		<cfset setModuleDelimiter(arguments.moduleDelimiter) />
		
		<cfreturn this />
 	</cffunction>

	<!---
	PUBLIC FUNCTIONS
	--->
	<cffunction name="isEmailAddress" access="public" output="false" returntype="boolean"
		hint="Checks if email address. Based on UDF:isEmail() from cflib.org<br/>
		Throws: nothing">
		<cfargument name="email_address" required="true" type="string" />
		<!--- Rewritten UDF from cflib.org Author: Jeff Guillaume (jeff@kazoomis.com) --->
		<cfreturn REFindNoCase("^['_a-z0-9-]+(\.['_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.(([a-z]{2,3})|(aero|coop|info|museum|name|jobs|travel))$", arguments.email_address) AND len(listGetAt(arguments.email_address, 1, "@")) LTE 64 AND len(listGetAt(arguments.email_address, 2, "@")) LTE 255 />
	</cffunction>

	<cffunction name="emailAntiSpam" access="public" returntype="string" output="false"
		hint="Rewrites an email address in HMTL entities to reduce spam harvesting.<br/>
		Throws: nothing">
		<cfargument name="emailAddress" type="string" required="true" />
		<!--- Rewritten UDF from cflib.org Author: Seth Duffey (sduffey@ci.davis.ca.us) --->
		<cfset var i = "" />
		<cfset var antiSpamEmail = "" />

		<cfloop from="1" to="#Len(EmailAddress)#" index="i">	
			<cfset antiSpamEmail = antiSpamEmail & "&##" & Asc(Mid(arguments.emailAddress,i,1)) & ";" />
		</cfloop>
		
		<cfreturn antiSpamEmail />
	</cffunction>

	<cffunction name="isUUID" access="public" output="false" returntype="boolean"
		hint="Check is value passed is a valid UUID.<br/>
		Throws: nothing">
		<cfargument name="value" required="true" type="string" />
		<cfreturn REFindNoCase("^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{16}$", arguments.value) />
	</cffunction>

	<cffunction name="parseCMSDatetime" access="public" returntype="date" output="false"
		hint="Parses a CMS (SVN or CVS) datetime into a CF datetime object.<br/>
		Throws: nothing">
		<cfargument name="cmsDatetime" type="string" required="true" />
		<!-- Replace all /'s that CVS uses with -'s --->
		<cfreturn LSParseDatetime(Replace(ListGetAt(arguments.cmsDatetime, 2, " "), "/", "-", "ALL") & " " & ListGetAt(arguments.cmsDatetime, 3, " ")) />
	</cffunction>

	<cffunction name="parseCMSRevision" access="public" returntype="numeric" output="false"
		hint="Parses a CMS (SVN or CVS) last changed revision string into a usable numerics.<br/>
		Throws: nothing">
		<cfargument name="cmsRevision" type="string" required="true" />
		<cfreturn ListGetAt(arguments.cmsRevision, 2, " ") />
	</cffunction>

	<cffunction name="stripHTML" access="public" returntype="string" output="false"
		hint="Removes HTML from the string.<br/>
		Throws: nothing">
		<cfargument name="str" type="string" required="true" />
		<!--- Rewritten UDF from cflib.org Author: Raymond Camden (ray@camdenfamily.com) --->
		<cfreturn REReplaceNoCase(str, "<[^>]*>", "", "ALL") />
	</cffunction>

	<cffunction name="decimalRound" access="public" returntype="string" output="false"
		hint="Round mode can be up, down or even. Even is the default.<br/>
		Throws: nothing">
		<cfargument name="numberToRound" type="numeric" required="true" />
		<cfargument name="numberOfPlaces" type="numeric" required="true" />
		<cfargument name="mode" type="string" required="false" default="even" />
		<!--- Thanks to Christian Cantrell's blog for this one --->
		<cfset var bd = CreateObject("java", "java.math.BigDecimal") />
		<cfset var result = "" />

		<cfset bd.init(arguments.numberToRound) />
		<cfif arguments.mode IS "up">
			<cfset bd = bd.setScale(arguments.numberOfPlaces, bd.ROUND_HALF_UP) />
		<cfelseif arguments.mode IS "down">
			<cfset bd = bd.setScale(arguments.numberOfPlaces, bd.ROUND_HALF_DOWN) />
		<cfelse>
			<cfset bd = bd.setScale(arguments.numberOfPlaces, bd.ROUND_HALF_EVEN) />
		</cfif>
		<cfset result = bd.toString() />
		
		<cfif result EQ 0>
			<cfset result = 0 />
		</cfif>

		<cfreturn  result />
	</cffunction>

	<cffunction name="differentChars" access="public" returntype="numeric" output="false"
		hint="Counts the number of different characters.<br/>
		Throws: nothing">
		<cfargument name="inputString" type="string" required="true" />
		<cfargument name="caseSensitive" type="boolean" required="false" default="FALSE" />
		<!--- Rewritten UDF from cflib.org Author: Bjorn Jensen (public.cflib@saghian.com) --->
		<cfset var iCount = 0 />
		<cfset var i = 0 />
		<cfset var sChars = "" />
		<cfset var sChar = "" />
		
		<cfif arguments.caseSensitive>
			<cfloop from="1" to="#Len(arguments.inputString)#" index="i">
				<cfset sChar = Mid(arguments.inputString, i, 1) />
				<cfif NOT Find(sChar, sChars)>
					<cfset sChars = sChars & sChar />
					<cfset iCount = iCount + 1 />
				</cfif>
			</cfloop>
		<cfelse>
			<cfloop from="1" to="#Len(arguments.inputString)#" index="i">
				<cfset sChar = Mid(arguments.inputString, i, 1) />
				<cfif NOT FindNoCase(sChar, sChars)>
					<cfset sChars = sChars & sChar />
					<cfset iCount = iCount + 1 />
				</cfif>
			</cfloop>
		</cfif>
	
		<cfreturn iCount />
	</cffunction>

	<cffunction name="countNumeric" access="public" returntype="numeric" output="false"
		hint="Returns the numeric count from the passed string. Requires differentChars UDF.<br/>
		Throws: nothing">
		<cfargument name="inputString" type="string"required="true"  />
		<cfreturn differentChars(REReplace(arguments.inputString, "([[:punct:]+[:alpha:]])", "", "ALL")) />
	</cffunction>
	
	<cffunction name="countAlpha" access="public" returntype="numeric" output="false"
		hint="Returns the alpha count from the passed string. Requires differentChars UDF.<br/>
		Throws: nothing">
		<cfargument name="inputString" type="string" required="true" />
		<cfargument name="caseSensitive" type="boolean" required="false" default="TRUE" />
		<cfreturn differentChars(REReplace(arguments.inputString, "([[:punct:]+[:digit:]])", "", "ALL"), arguments.caseSensitive) />
	</cffunction>

	<cffunction name="isListInList" access="public" returntype="boolean" output="false"
		hint="Checks is all elements of a list X is found in a list Y.">
		<cfargument name="list1" type="string" required="true" />
		<cfargument name="list2" type="string" required="true" />
		<cfargument name="delim1" type="string" required="false" default="," />
		<cfargument name="delim2" type="string" required="false" default="," />
		<cfargument name="matchOne" type="boolean" required="false" default="FALSE" />
		<cfargument name="caseSensitivity" type="boolean" required="false" default="FALSE" />
		<!--- Rewritten UDF from cflib.org Author: Daniel Chicayban (daniel@chicayban.com.br) --->
		<cfset var i = "" />
		<cfset var l = ListLen(arguments.list1, arguments.delim1) />
		<cfset var operationType = "" />
		<cfset var result = "" />
		
		<cfif arguments.matchOne AND arguments.caseSensitivity>
			<cfset operationType = "MatchOne/CaseSensitive" />
		<cfelseif arguments.matchOne AND NOT arguments.caseSensitivity>
			<cfset operationType = "MatchOne/NoCaseSensitive" />
		<cfelseif NOT arguments.matchOne AND arguments.caseSensitivity>
			<cfset operationType = "NoMatchOne/CaseSensitive" />
		<cfelseif NOT arguments.matchOne AND NOT arguments.caseSensitivity>
			<cfset operationType = "NoMatchOne/NoCaseSensitive" />
		</cfif>
		
		<cfswitch expression="#operationType#">
			<cfcase value="MatchOne/CaseSensitive">
				<cfset result = FALSE />
				<cfloop from="1" to="#l#" index="i">
					<cfif ListFind(arguments.list2, ListGetAt(arguments.list1, i, arguments.delim1), arguments.delim2)>
						<cfset result = TRUE />
						<cfbreak />
					</cfif>
				</cfloop>
			</cfcase>
			<cfcase value="MatchOne/NoCaseSensitive">
				<cfset result = FALSE />
				<cfloop from="1" to="#l#" index="i">
					<cfif ListFindNoCase(arguments.list2, ListGetAt(arguments.list1, i, arguments.delim1), arguments.delim2)>
						<cfset result = TRUE />
						<cfbreak />
					</cfif>
				</cfloop>
			</cfcase>
			<cfcase value="NoMatchOne/CaseSensitive">
				<cfset result = TRUE />
				<cfloop from="1" to="#l#" index="i">
					<cfif NOT ListFind(arguments.list2, ListGetAt(arguments.list1, i, arguments.delim1), arguments.delim2)>
						<cfset result = FALSE />
						<cfbreak />
					</cfif>
				</cfloop>
			</cfcase>
			<cfcase value="NoMatchOne/NoCaseSensitive">
				<cfset result = TRUE />
				<cfloop from="1" to="#l#" index="i">
					<cfif NOT ListFindNoCase(arguments.list2, ListGetAt(arguments.list1, i, arguments.delim1), arguments.delim2)>
						<cfset result = FALSE />
						<cfbreak />
					</cfif>
				</cfloop>
			</cfcase>
		</cfswitch>

		<cfreturn result />
	</cffunction>

	<cffunction name="XHTMLParagraphFormat" access="public" returntype="string" output="false"
		hint="Returns a XHTML compliant string wrapped with properly formatted paragraph tags.">
		<cfargument name="input" type="string" required="true" />
		<cfargument name="attribute" type="string" required="false" default="" />
		<!--- Rewritten UDF from cflib.org Author: Jeff Howden (jeff@members.evolt.org) --->
		<cfset var result = "" />
		<cfset var string = Trim(arguments.input) />
		<cfset var emptyParagraph = "" />
				
		<cfif Len(arguments.attribute)>
			<cfset arguments.attribute = " " & arguments.attribute />
		</cfif>
		
		<cfset emptyParagraph = "<p" & arguments.attribute & "></p>" />
		
		<cfif Len(string)>
			<cfset result = "<p" & arguments.attribute & ">" & Replace(string, Chr(13) & Chr(10), "</p>" & Chr(13) & Chr(10) & "<p" & arguments.attribute & ">", "ALL") & "</p>" />
			<!--- Remove empty P elements produced by double returns --->
			<cfset result = Replace(result, emptyParagraph, "", "ALL") />
		</cfif>		
		
		<cfreturn result />
	</cffunction>

	<cffunction name="isTrueNumeric" access="public" returntype="boolean" output="false"
		hint="Returns true if all characters in a string are numeric.<br/>
		Throws: nothing.">
		<cfargument name="str" type="string" required="true"
			hint="String to check.">
		<cfreturn ReFind("[^0-9]", arguments.str) IS 0 />
	</cffunction>

	<cffunction name="randString" access="public" returntype="string" output="false"
		hint="Returns a random string according to the type.">
		<cfargument name="type" type="string" required="true" />
		<cfargument name="count" type="numeric" required="true" />
		<cfargument name="custList" type="string" required="false" />

		<cfset var randStr = "" />
		<cfset var alpha_lcase = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z" />
		<cfset var alpha_ucase = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z" />
		<cfset var num = "0,1,2,3,4,5,6,7,8,9" />
		<cfset var secure = "!,@,$,%,&,*,-,_,=,+,?,~" />
		<cfset var rangeMax = "" />
		<cfset var useList = "" />
		<cfset var randChar = "" />
		<cfset var i = "" />
				
		<cfswitch expression="#arguments.type#">
			<cfcase value="alpha">
				<cfset useList = alpha_lcase & "," & alpha_ucase />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfcase value="alphaLCase">
				<cfset useList = alpha_lcase />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfcase value="alphaUCase">
				<cfset useList = alpha_ucase />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfcase value="alphaNum">
				<cfset useList = alpha_lcase & "," & alpha_ucase & "," & num />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfcase value="alphaNumLCase">
				<cfset useList = alpha_lcase & "," & "," & num />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfcase value="secure">
				<cfset useList = alpha_lcase & "," & alpha_ucase & "," & num & "," & secure />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfcase value="custList">
				<cfset useList = arguments.useCustList />
				<cfset rangeMax = ListLen(useList) />
			</cfcase>
			<cfdefaultcase>
				<cfset useList = num />
				<cfset rangeMax = ListLen(useList) />
			</cfdefaultcase>
		</cfswitch>

		<cfloop from="1" to="#arguments.count#" index="i">
			<cfset randChar = RandRange(1, rangeMax) />
			<cfset randStr = randStr & ListGetAt(useList, randChar) />
		</cfloop>

		<cfreturn randStr />
	</cffunction>

	<cffunction name="sha1" access="public" returntype="string" output="false"
		hint="Computes a SHA1 message digest.">
		<cfargument name="message" type="string" required="true" />
		<!--- Rewritten UDF from cflib.org Author: Rob Brooks-Bilson (rbils@amkor.com) --->
		<cfset var hex_msg = "" />
		<cfset var hex_msg_len = 0 />
		<cfset var padded_hex_msg = "" />
		<cfset var msg_block = "" />
		<cfset var num = 0 />
		<cfset var temp = "" />
		<cfset var h = ArrayNew(1) />
		<cfset var w = ArrayNew(1) />
		<cfset var a = "" />
		<cfset var b = "" />
		<cfset var c = "" />
		<cfset var d = "" />
		<cfset var e = "" />
		<cfset var f = "" />
		<cfset var i = 0 />
		<cfset var k = "" />
		<cfset var n = 0 />
		<cfset var t = 0 />
		
		<!--- Convert the msg to ASCII binary-coded form --->
		<cfloop from="1" to="#Len(arguments.message)#" index="i">
			<cfset hex_msg = hex_msg & Right("0" & FormatBaseN(Asc(Mid(message, i , 1)), 16), 2)>
		</cfloop>

		<!--- Compute the msg length in bits --->
		<cfset hex_msg_len = FormatBaseN(8 * Len(message), 16) />
		
		<!--- Pad the msg to make it a multiple of 512 bits long --->
		<cfset padded_hex_msg = hex_msg & "80" & RepeatString("0", 128 - ((Len(hex_msg) + 2 + 16) Mod 128)) & RepeatString("0", 16 - Len(hex_msg_len)) & hex_msg_len />

		<!--- Initialize the buffers --->
		<cfset h[1] = InputBaseN("0x67452301", 16) />
		<cfset h[2] = InputBaseN("0xefcdab89", 16) />
		<cfset h[3] = InputBaseN("0x98badcfe", 16) />
		<cfset h[4] = InputBaseN("0x10325476", 16) />
		<cfset h[5] = InputBaseN("0xc3d2e1f0", 16) />

		<!--- Process the msg 512 bits at a time --->
		<cfloop from="1" to="#Evaluate(Len(padded_hex_msg)/128)#" index="n">
			<cfset msg_block = Mid(padded_hex_msg, 128 * (n-1) + 1, 128) />
			
			<cfset a = h[1] />
			<cfset b = h[2] />
			<cfset c = h[3] />
			<cfset d = h[4] />
			<cfset e = h[5] />
			
			<cfloop from="0" to="79" index="t">
				<!--- nonlinear functions and constants --->
				<cfif t LTE 19>
					<cfset f = BitOr(BitAnd(b, c), BitAnd(BitNot(b), d)) />
					<cfset k = InputBaseN("0x5a827999", 16) />
				<cfelseif t LTE 39>
					<cfset f = BitXor(BitXor(b, c), d) />
					<cfset k = InputBaseN("0x6ed9eba1", 16) />
				<cfelseif t LTE 59>
					<cfset f = BitOr(BitOr(BitAnd(b, c), BitAnd(b, d)), BitAnd(c, d)) />
					<cfset k = InputBaseN("0x8f1bbcdc", 16) />
				<cfelse>
					<cfset f = BitXor(BitXor(b, c), d) />
					<cfset k = InputBaseN("0xca62c1d6", 16) />
				</cfif>
				
				<!--- Transform the msg block from 16 32-bit words to 80 32-bit words --->
				<cfif t LTE 15>
					<cfset w[t + 1] = InputBaseN(Mid(msg_block, 8 * t + 1, 8), 16) />
				<cfelse>
					<cfset num = BitXor(BitXor(BitXor(w[t - 3 + 1], w[t - 8 + 1]), w[t -14 + 1]), w[t - 16 + 1]) />
					<cfset w[t + 1] = BitOr(BitSHLN(num, 1), BitSHRN(num, 32 - 1)) />
				</cfif>
				
				<cfset temp = BitOr(BitSHLN(a, 5), BitSHRN(a, 32 - 5)) + f + e + w[t + 1] + k />
				<cfset e = d />
				<cfset d = c />
				<cfset c = BitOr(BitSHLN(b, 30), BitSHRN(b, 32 - 30)) />
				<cfset b = a />
				<cfset a = temp />
				<cfset num = a />
				
				<cfloop condition="(num LT -2^31) OR (num GE 2^31)">
					<cfset num = num - Sgn(num) * 2 ^ 32 />
				</cfloop>
				
				<cfset a = num />
			</cfloop>
			
			<cfset h[1] = h[1] + a />
			<cfset h[2] = h[2] + b />
			<cfset h[3] = h[3] + c />
			<cfset h[4] = h[4] + d />
			<cfset h[5] = h[5] + e />
			
			<cfloop from="1" to="5" index="i">
				<cfloop condition="(h[i] LT -2 ^ 31) OR (h[i] GTE 2 ^ 31)">
					<cfset h[i] = h[i] - Sgn(h[i])* 2 ^ 32 />
				</cfloop>
			</cfloop>
		</cfloop>
		
		<cfloop from="1" to="5" index="i">
			<cfset h[i] = RepeatString("0", 8 - Len(FormatBaseN(h[i], 16))) & UCase(FormatBaseN(h[i], 16)) />
		</cfloop>
		
		<cfreturn h[1] & h[2] & h[3] & h[4] & h[5] />
	</cffunction>
	
	<!--- modified from Ray Camden's BlogCFC (http://www.blogcfc.com) --->
	<cffunction name="renderEntry" access="public" output="false" returntype="string" 
			hint="Renders blog entries by calling other formatting functions">
		<cfargument name="content" type="string" required="true" />
		<cfargument name="formatParagraph" type="string" required="false" default="true" />
		
		<cfset var counter = "" />
		<cfset var codeblock = "" />
		<cfset var codeportion = "" />
		<cfset var result = "" />
		<cfset var newbody = "" />
		<cfset var style = "" />
		<cfset var newContent = "" />
		
		<cfif findNoCase("<code>", arguments.content) and findNoCase("</code>", arguments.content)>
			<cfset counter = findNoCase("<code>", arguments.content) />
			<cfloop condition="counter gte 1">
                <cfset codeblock = reFindNoCase("(?s)(.*)(<code>)(.*)(</code>)(.*)", arguments.content, 1, 1) /> 
				<cfif arrayLen(codeblock.len) gte 6>
                    <cfset codeportion = mid(arguments.content, codeblock.pos[4], codeblock.len[4]) />
                    <cfif len(trim(codeportion))>
						<cfset style = "code" />
						<cfset result = coloredCode(codeportion, style) />
					<cfelse>
						<cfset result = "" />
					</cfif>
					<cfset newbody = mid(arguments.content, 1, codeblock.len[2]) & result & 
										mid(arguments.content, codeblock.pos[6], codeblock.len[6]) />
	
                    <cfset arguments.content = newbody />
					<cfset counter = findNoCase("<code>", arguments.content, counter) />
				<cfelse>
					<!--- bad crap, maybe <code> and no ender, or maybe </code><code> --->
					<cfset counter = 0 />
				</cfif>
			</cfloop>
		</cfif>
		
		<cfif arguments.formatParagraph>
			<cfreturn ParagraphFormat(arguments.content) />
		<cfelse>
			<cfreturn arguments.content />
		</cfif>
	</cffunction>

	<!--- 
	*** TAKEN FROM RAY CAMDEN'S BLOG CFC (http://www.blogcfc.com) ***
	Copyright for coloredCode function. Also note that Jeff Coughlin made some mods to this as well.
	=============================================================
		Utility:	ColdFusion ColoredCode v3.2
		Author:		Dain Anderson
		Email:		webmaster@cfcomet.com
		Revised:	June 7, 2001
		Download:	http://www.cfcomet.com/cfcomet/utilities/
	============================================================= 
	--->
	<cffunction name="coloredCode" access="public" output="false" returnType="string" 
			   hint="Colors code">
		<cfargument name="dataString" type="string" required="true">
		<cfargument name="class" type="string" required="true">

		<cfset var data = trim(arguments.dataString) />
		<cfset var eof = 0>
		<cfset var bof = 1>
		<cfset var match = "">
		<cfset var orig = "">
		<cfset var chunk = "">

		<cfscript>
		/* Convert special characters so they do not get interpreted literally; italicize and boldface */
		data = REReplaceNoCase(data, '&([[:alpha:]]{2,});', '�strong��em�&amp;\1;�/em��/strong�', 'ALL');
	
		/* Convert many standalone (not within quotes) numbers to blue, ie. myValue = 0 */
		data = REReplaceNoCase(data, "(gt|lt|eq|is|,|\(|\))([[:space:]]?[0-9]{1,})", "\1�span style='color: ##0000ff'�\2�/span�", "ALL");
	
		/* Convert normal tags to navy blue */
		data = REReplaceNoCase(data, "<(/?)((!d|b|c(e|i|od|om)|d|e|f(r|o)|h|i|k|l|m|n|o|p|q|r|s|t(e|i|t)|u|v|w|x)[^>]*)>", "�span style='color: ##000080'�<\1\2>�/span�", "ALL");
	
		/* Convert all table-related tags to teal */
		data = REReplaceNoCase(data, "<(/?)(t(a|r|d|b|f|h)([^>]*)|c(ap|ol)([^>]*))>", "�span style='color: ##008080'�<\1\2>�/span�", "ALL");
	
		/* Convert all form-related tags to orange */
		data = REReplaceNoCase(data, "<(/?)((bu|f(i|or)|i(n|s)|l(a|e)|se|op|te)([^>]*))>", "�span style='color: ##ff8000'�<\1\2>�/span�", "ALL");
	
		/* Convert all tags starting with 'a' to green, since the others aren't used much and we get a speed gain */
		data = REReplaceNoCase(data, "<(/?)(a[^>]*)>", "�span style='color: ##008000'�<\1\2>�/span�", "ALL");
	
		/* Convert all image and style tags to purple */
		data = REReplaceNoCase(data, "<(/?)((im[^>]*)|(sty[^>]*))>", "�span style='color: ##800080'�<\1\2>�/span�", "ALL");
	
		/* Convert all ColdFusion, SCRIPT and WDDX tags to maroon */
		data = REReplaceNoCase(data, "<(/?)((cf[^>]*)|(sc[^>]*)|(wddx[^>]*))>", "�span style='color: ##800000'�<\1\2>�/span�", "ALL");
	
		/* Convert all inline "//" comments to gray (revised) */
		data = REReplaceNoCase(data, "([^:/]\/{2,2})([^[:cntrl:]]+)($|[[:cntrl:]])", "�span style='color: ##808080'��em�\1\2�/em��/span�", "ALL");
	
		/* Convert all multi-line script comments to gray */
		data = REReplaceNoCase(data, "(\/\*[^\*]*\*\/)", "�span style='color: ##808080'��em�\1�/em��/span�", "ALL");
	
		/* Convert all HTML and ColdFusion comments to gray */	
		/* The next 10 lines of code can be replaced with the commented-out line following them, if you do care whether HTML and CFML 
		   comments contain colored markup. */

		while(NOT EOF) {
			Match = REFindNoCase("<!--" & "-?([^-]*)-?-->", data, BOF, True);
			if (Match.pos[1]) {
				Orig = Mid(data, Match.pos[1], Match.len[1]);
				Chunk = REReplaceNoCase(Orig, "�(/?[^�]*)�", "", "ALL");
				BOF = ((Match.pos[1] + Len(Chunk)) + 38); // 38 is the length of the SPAN tags in the next line
				data = Replace(data, Orig, "�span style='color: ##808080'��em�#Chunk#�/em��/span�");
			} else EOF = 1;
		}


		/* Convert all quoted values to blue */
		data = REReplaceNoCase(data, """([^""]*)""", "�span style=""color: ##0000ff""�""\1""�/span�", "all");

		/* Convert left containers to their ASCII equivalent */
		data = REReplaceNoCase(data, "<", "&lt;", "ALL");

		/* Convert right containers to their ASCII equivalent */
		data = REReplaceNoCase(data, ">", "&gt;", "ALL");

		/* Revert all pseudo-containers back to their real values to be interpreted literally (revised) */
		data = REReplaceNoCase(data, "�([^�]*)�", "<\1>", "ALL");

		/* ***New Feature*** Convert all FILE and UNC paths to active links (i.e, file:///, \\server\, c:\myfile.cfm) */
		data = REReplaceNoCase(data, "(((file:///)|([a-z]:\\)|(\\\\[[:alpha:]]))+(\.?[[:alnum:]\/=^@*|:~`+$%?_##& -])+)", "<a target=""_blank"" href=""\1"">\1</a>", "ALL");

		/* Convert all URLs to active links (revised) */
		data = REReplaceNoCase(data, "([[:alnum:]]*://[[:alnum:]\@-]*(\.[[:alnum:]][[:alnum:]-]*[[:alnum:]]\.)?[[:alnum:]]{2,}(\.?[[:alnum:]\/=^@*|:~`+$%?_##&-])+)", "<a target=""_blank"" href=""\1"">\1</a>", "ALL");

		/* Convert all email addresses to active mailto's (revised) */
		data = REReplaceNoCase(data, "(([[:alnum:]][[:alnum:]_.-]*)?[[:alnum:]]@[[:alnum:]][[:alnum:].-]*\.[[:alpha:]]{2,})", "<a href=""mailto:\1"">\1</a>", "ALL");
		</cfscript>

		<!--- mod by ray --->
		<!--- change line breaks at end to <br /> --->
		<cfset data = replace(data,chr(13),"<br />","all") />
		<!--- replace tab with 3 spaces --->
		<cfset data = replace(data,chr(9),"&nbsp;&nbsp;&nbsp;","all") />
		<cfset data = "<div class=""#arguments.class#"">" & data &  "</div>" />
		
		<cfreturn data>
	</cffunction>
	
	<cffunction name="stripSmartQuotes" access="public" output="false" returntype="string" 
			hint="Strips all the MS Word 'smart quotes' and other smart characters from a string">
		<cfargument name="stringToFix" type="string" required="true" />
		
		<cfset var fixedString = "" />
		      
		<!--- This line taken from Nathan Dintenfas' SafeText UDF --->
		<!--- www.cflib.org/udf.cfm/safetext --->
		<cfset fixedString = replaceList(arguments.stringToFix, chr(8216) & "," & chr(8217) & "," & chr(8220) & "," & chr(8221) & "," 
										& chr(8212) & "," & chr(8213) & "," & chr(8230), "',',"","",--,--,...") />
		<cfreturn fixedString />
	</cffunction>
	
	<cffunction name="buildUrl" access="public" returntype="string" output="false"
		hint="Builds a framework specific url.">
		<cfargument name="moduleName" type="string" required="true"
			hint="Name of the module to build the url with." />
		<cfargument name="eventName" type="string" required="true"
			hint="Name of the event to build the url with." />
		<cfargument name="urlParameters" type="any" required="false" default=""
			hint="Name/value pairs (urlArg1=value1|urlArg2=value2) to build the url with or a struct of data." />
		<cfargument name="urlBase" type="string" required="false" default=""
			hint="Base of the url. Defaults to zero length strin." />
		
		<cfset var builtUrl = "" />
		<cfset var params = parseBuildUrlParameters(arguments.urlParameters) />
		<cfset var module = "" />
		<cfset var i = "" />
		
		<!--- Append the base url --->
		<cfif NOT Len(arguments.urlBase)>
			<cfset builtUrl = getBlogUrl() & getDefaultUrlBase() />
		<cfelse>
			<cfset builtUrl = arguments.urlBase />
		</cfif>

		<!--- Attach the module/event name if defined --->
		<cfif Len(arguments.moduleName)>
			<cfset builtUrl = builtUrl & getQueryStringDelimiter() & getEventParameter() & getPairDelimiter() & arguments.moduleName & getModuleDelimiter() & arguments.eventName />
		<cfelseif NOT Len(arguments.moduleName)>
			<cfset builtUrl = builtUrl & getQueryStringDelimiter() & getEventParameter() & getPairDelimiter()& arguments.eventName />
		</cfif>
		
		<!--- Attach each additional arguments if it exists and is a simple value --->
		<cfloop collection="#params#" item="i">
			<cfif IsSimpleValue(params[i])>
				<cfset builtUrl = builtUrl & getSeriesDelimiter() & i & getPairDelimiter() & URLEncodedFormat(params[i]) />
			</cfif>
		</cfloop>
		
		<cfreturn builtUrl />
	</cffunction>

	<!---
	PROTECTED FUNCTIONS
	--->	
	<cffunction name="parseBuildUrlParameters" access="private" returntype="struct" output="false"
		hint="Parses the build url parameters into a useable form.">
		<cfargument name="urlParameters" type="any" required="true"
			hint="Takes string of name/value pairs or a struct.">
		
		<cfset var params = StructNew() />
		<cfset var temp = "" />
		<cfset var i = "" />
		
		<cfif IsSimpleValue(arguments.urlParameters)>
			<cfloop list="#arguments.urlParameters#" index="i" delimiters="|">
				<cfif ListLen(i, "=") EQ 2>
					<cfset temp = ListLast(i, "=") />
				<cfelse>
					<cfset temp = "" />
				</cfif>
				<cfset params[ListFirst(i, "=")] = temp />
			</cfloop>
		<cfelseif IsStruct(arguments.urlParameters)>
			<cfset params = arguments.urlParameters />
		<cfelse>
			<cfthrow
				type="urlParametersInvalidType"
				message="BuildUrl()'s parameters attribute takes a list or struct."/>
		</cfif>
		
		<cfreturn params />
	</cffunction>

	<!---
	ACCESSORS
	--->
	<cffunction name="setEventParameter" access="private" returntype="void" output="false">
		<cfargument name="eventParameter" type="string" required="true" />
		<cfset variables.instance.eventParameter = arguments.eventParameter />
	</cffunction>
	<cffunction name="getEventParameter" access="private" returntype="string" output="false">
		<cfreturn variables.instance.eventParameter />
	</cffunction>
	
	<cffunction name="setBlogUrl" access="private" returntype="void" output="false">
		<cfargument name="blogUrl" type="string" required="true" />
		<cfset variables.instance.blogUrl = arguments.blogUrl />
	</cffunction>
	<cffunction name="getBlogUrl" access="private" returntype="string" output="false">
		<cfreturn variables.instance.blogUrl />
	</cffunction>
	
	<cffunction name="setDefaultUrlBase" access="private" returntype="void" output="false">
		<cfargument name="defaultUrlBase" type="string" required="true" />
		<cfset variables.instance.defaultUrlBase = arguments.defaultUrlBase />
	</cffunction>
	<cffunction name="getDefaultUrlBase" access="private" returntype="string" output="false">
		<cfreturn variables.instance.defaultUrlBase />
	</cffunction>
	
	<cffunction name="setQueryStringDelimiter" access="private" returntype="void" output="false">
		<cfargument name="queryStringDelimiter" type="string" required="true" />
		<cfset variables.instance.queryStringDelimiter = arguments.queryStringDelimiter />
	</cffunction>
	<cffunction name="getQueryStringDelimiter" access="private" returntype="string" output="false">
		<cfreturn variables.instance.queryStringDelimiter />
	</cffunction>

	<cffunction name="setSeriesDelimiter" access="private" returntype="void" output="false">
		<cfargument name="seriesDelimiter" type="string" required="true" />
		<cfset variables.instance.seriesDelimiter = arguments.seriesDelimiter />
	</cffunction>
	<cffunction name="getSeriesDelimiter" access="private" returntype="string" output="false">
		<cfreturn variables.instance.seriesDelimiter />
	</cffunction>
	
	<cffunction name="setPairDelimiter" access="private" returntype="void" output="false">
		<cfargument name="pairDelimiter" type="string" required="true" />
		<cfset variables.instance.pairDelimiter = arguments.pairDelimiter />
	</cffunction>
	<cffunction name="getPairDelimiter" access="private" returntype="string" output="false">
		<cfreturn variables.instance.pairDelimiter />
	</cffunction>
	
	<cffunction name="setModuleDelimiter" access="private" returntype="void" output="false">
		<cfargument name="moduleDelimiter" type="string" required="true" />
		<cfset variables.instance.moduleDelimiter = arguments.moduleDelimiter />
	</cffunction>
	<cffunction name="getModuleDelimiter" access="private" returntype="string" output="false">
		<cfreturn variables.instance.moduleDelimiter />
	</cffunction>
	
</cfcomponent>